#include <kernel.h>
#include <pa4.h>
//#include <disk.h>

extern int dskread(struct devsw *pdev, char *buffer, int block_no, int count);
extern int dskwrite(struct devsw *pdev, char *buffer, int block_no, int count);
extern int memncpy(void *dest, void *src, int num);
/*
 * Part A 3/4. buf_fetch()
 * buf_fetch() does:
 *     if there is a buffer that already store the block data of block_no,
 *         then return the buffer.
 *     otherwise
 *         read the block of block_no, store it on a buffer, and return the buffer.
 * 
 * parameters:
 *     pdev:		device descriptor
 *     block_no: 	a block number to read
 *     policy: 		buffer replacement policy
 */


void print_buf()
{

dsk_buffer_p i = buf_head;

kprintf("Buffer Content\n");

while(i!=NULL)
        {
          kprintf("%d  ",i->block_no);
          i=i->next;
        }
kprintf("Buf count: %d\n",buf_count);
}



dsk_buffer_p buf_search(int block_no,int pdev)
{

dsk_buffer_p temp;

temp = buf_head;
	while(temp != NULL)
	{
		if( (temp->block_no == block_no) && (temp->pdev==pdev)) 
		{
			//return temp;
			break;			
		}
		temp=temp->next;
	}
	return temp;
}



void eviction(int count)

{
int i;
dsk_buffer_p prev,tail;

	for(i=0;i<count;i++)
	{
	prev = buf_head;
	tail = buf_head;
		while(tail->next!= NULL)
		{
			prev=tail;
			tail=tail->next;
		}

	prev->next=NULL;
	kprintf("\tEvicting block no: %d from buffer\n",tail->block_no);

	if(tail->dirty == 1)	
		{
			//invalid[block_no]=1;	//
			dskwrite(tail->pdev,tail->data,tail->block_no,1);
			//invalid[block_no]=0;	//
		}
	freemem(tail,sizeof(struct buf));	//sizeof(dsk_buffer_p));
	buf_count--;

	}

}//eviction done

//*****************************************************

dsk_buffer_p dsk_fetch_ins(struct devsw *pdev,int block_no)
{

int i;
dsk_buffer_p blk,temp;
int evec_count;
char *buffer;	//char or word

evec_count=buf_count+1+PA4_PREFETCH_SIZE-PA4_BUFFER_SIZE;

if (evec_count > 0)
	{
	//evec_count=buf_count+1+PA4_PREFETCH_SIZE-PA4_BUFFER_SIZE;
	eviction(evec_count);
	}

buffer=getmem(128*(PA4_PREFETCH_SIZE+1));	//need to confirm

dskread(pdev,buffer,block_no,PA4_PREFETCH_SIZE+1);	//this guy returns int  whta do with it???

	for(i=0;i<PA4_PREFETCH_SIZE+1;i++)	//insering into the list
	{
		if(buf_search(block_no+i,pdev)== NULL)
			{
					temp=getmem(sizeof(struct buf));
					temp->block_no = block_no+i;
					temp->pdev = pdev;
					temp->size = 128;
//(((disk_desc *)(((struct devsw *)bufObj->pdev)->dvioblk))->block_size);
					temp->data = getmem(128);
					memncpy(temp->data,buffer+(i*128),128);
					temp->next = buf_head;
					temp->dirty=0;
					buf_head = temp;
					buf_count++;
					if (i==0)
					{
						blk = temp;	
					}

					kprintf("\tBlock no %d fetched\n",temp->block_no);

			}
		else 
			{
				kprintf("\tBlock no %d already in buffer hence not fetching\n", block_no+i);
			}


	}
kprintf("\tRequested block fetched and others prefetched if not already in buffer\n");
return blk;
}


//--------------------------


void updt_head(int block_no)
{
dsk_buffer_p temp,prev;

	if(buf_head->block_no == block_no)
	{
		return;
	}
	else
	{
	temp=buf_head;
	while(temp->block_no!=block_no)
		{
			prev=temp;
			temp=temp->next;
		}
	prev->next=temp->next;
	temp->next=buf_head;
	buf_head=temp;
	return;
	}
}




dsk_buffer_p buf_fetch(struct devsw *pdev, int block_no, int policy) //policy is LRU/FIFO
{

//STATWORD ps;
dsk_buffer_p buf_blk;
//int dskrd_ret;

//disable(ps);

kprintf("\tInisde buf fetch buffer count = %d\n",buf_count);
    
     if(pdev==&devtab[DISK0])
     { 		
	while(invalid[block_no] == 1)
	{
	kprintf("\tAnother process has already requested for block no %d hence suspending this process till the block is fetched\n",block_no);
	
	resched();
	}

	invalid[block_no] = 1;
     }
     else 
     {
        while(invalid2[block_no] == 1)
        {
        kprintf("\tAnother process has already requested for block no %d hence suspending this process till the block is fetched\n",block_no);

        resched();
        }

        invalid2[block_no] = 1;

     }	



/*
	if( (block_no < 0) || (block_no > (((disk_desc *)(((struct devsw *)bufObj->pdev)->dvioblk))->logical_blocks) ))
	{		
		//restore(ps);
		return INVALID_BLOCK;
	}
*/

	buf_blk = buf_search(block_no,pdev);
	
	if (buf_blk != NULL) //HIT
	{
		if(policy == POLICY_LRU)
		{
			updt_head(block_no);	//update the head of the linked list
		}
	
	kprintf("Hit for block no %d\n",block_no);
	if(pdev==&devtab[DISK0])
	invalid[block_no]=0;
	else 
	invalid2[block_no]=0;
	//restore(ps);
	print_buf();
	return buf_blk; // return the pointer to the block req which is of type dsk_buffer_p 
	}

	else if (buf_blk == NULL) // MISS
	{
	
	kprintf("Miss for block no %d\n",block_no);	
	buf_blk = dsk_fetch_ins(pdev,block_no);
	//restore(ps);
	
	kprintf("\tBlock fetched from disk\n");
	if(pdev==&devtab[DISK0])	
	invalid[block_no] = 0;
	else
	invalid2[block_no] = 0;
	print_buf();
	return buf_blk; 	

	} // end of else 
	

}	

